home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / TURBOPASCAL WIN / OWLDEMOS.PAK / DDEMLSRV.PAS < prev    next >
Pascal/Delphi Source File  |  1992-06-08  |  16KB  |  538 lines

  1. {***************************************************}
  2. {                                                   }
  3. {   Turbo Pascal for Windows                        }
  4. {   Windows 3.1 DDEML Demonstration Program         }
  5. {                                                   }
  6. {   Copyright (c) 1992 by Borland International     }
  7. {                                                   }
  8. {***************************************************}
  9.  
  10. program DDEMLServer;
  11.  
  12. { This sample application uses the DDEML library in the server side of a
  13.   cooperative application.  This server is a simple data-entry application
  14.   which allows an operator to enter three data items, which are made
  15.   available through DDE to interested clients.
  16.  
  17.   This server makes its service available under the following names:
  18.  
  19.        Service: 'DataEntry'
  20.        Topic  : 'SampledData'
  21.        Items  : 'DataItem1', 'DataItem2', 'DataItem3'
  22.  
  23.   Conceivably, other topics under this service could be defined.  Things
  24.   such as historical data, information about the sampling, and so on
  25.   might make useful topics.
  26.  
  27.   You must run this server BEFORE running the client (DDEMLCLI.PAS), or
  28.   the client will fail the connection.
  29.  
  30.   The interface to this server is defined by the list of names (Service,
  31.   Topic, and Items) in the separate unit called DataEntry (DATAENTR.TPU).
  32.   The server makes the Items available in cf_Text format; they can be
  33.   converted and stored locally as integers by the client.  
  34. }
  35.  
  36. uses Strings, WinTypes, WinProcs, WObjects, Win31, DDEML, ShellAPI, BWCC,
  37.   DataEntry;
  38.  
  39. {$R DDEMLSRV}
  40.  
  41. const
  42.  
  43. { Resource IDs }
  44.  
  45.   id_Menu    = 100;
  46.   id_About   = 100;
  47.   id_Icon    = 100;
  48.  
  49.   id_Value1  = 401;  { Used with the DataEntry Dialog }
  50.   id_Value2  = 402;
  51.   id_Value3  = 403;
  52.  
  53.   st_Message =   1;
  54.  
  55. { Menu command IDs }
  56.  
  57.   cm_DataEnter = 201;
  58.   cm_DataClear = 202;
  59.   cm_HelpAbout = 300;
  60.  
  61. type
  62.  
  63. { Application main window }
  64.  
  65.   PDDEServerWindow = ^TDDEServerWindow;
  66.   TDDEServerWindow = object(TWindow)
  67.     Inst       : Longint;
  68.     CallBack   : TCallback;
  69.     ServiceHSz : HSz;
  70.     TopicHSz   : HSz;
  71.     ItemHSz    : array [1..NumValues] of HSz;
  72.     ConvHdl    : HConv;
  73.     Advising   : array [1..NumValues] of Boolean;
  74.  
  75.     DataSample : TDataSample;
  76.  
  77.     constructor Init(AParent: PWindowsObject; ATitle: PChar);
  78.     destructor  Done; virtual;
  79.  
  80.     procedure GetWindowClass(var AWndClass: TWndClass); virtual;
  81.     function  GetClassName: PChar; virtual;
  82.     procedure SetupWindow; virtual;
  83.     procedure Paint(PaintDC: HDC; var PaintInfo: TPaintStruct); virtual;
  84.  
  85.     procedure CMDataEnter(var Msg: TMessage);
  86.       virtual cm_First + cm_DataEnter;
  87.     procedure CMDataClear(var Msg: TMessage);
  88.       virtual cm_First + cm_DataClear;
  89.     procedure CMHelpAbout(var Msg: TMessage);
  90.       virtual cm_First + cm_HelpAbout;
  91.  
  92.     function  MatchTopicAndService(Topic, Service: HSz): Boolean; virtual;
  93.     function  MatchTopicAndItem(Topic, Item: HSz): Integer; virtual;
  94.     function  WildConnect(Topic, Service: HSz;
  95.       ClipFmt: Word): HDDEData; virtual;
  96.     function  AcceptPoke(Item: HSz; ClipFmt: Word;
  97.       Data: HDDEData): Boolean; virtual;
  98.     function  DataRequested(TransType: Word; ItemNum: Integer;
  99.       ClipFmt: Word): HDDEData; virtual;
  100.   end;
  101.  
  102.  
  103. { Application object }
  104.  
  105.   TDDEServerApp = object(TApplication)
  106.     procedure InitMainWindow; virtual;
  107.   end;
  108.  
  109.  
  110. { Initialized globals }
  111.  
  112. const
  113.   DemoTitle   : PChar = 'DDEML Demo, Server Application';
  114.  
  115.   MaxAdvisories = 100;
  116.   NumAdvLoops : Integer = 0;
  117.  
  118.  
  119. { Global variables }
  120.  
  121. var
  122.   App: TDDEServerApp;
  123.  
  124.  
  125. { Local Function: CallBack Procedure for DDEML }
  126.  
  127. { This callback procedure responds to all transactions generated by the
  128.   DDEML.  The target Window object is obtained from the stored global,
  129.   and the appropriate methods within that objects are used to respond
  130.   to the given transaction, as indicated by the CallType parameter.
  131. }
  132. function CallbackProc(CallType, Fmt: Word; Conv: HConv; HSz1, HSz2: HSZ;
  133.   Data: HDDEData; Data1, Data2: Longint): HDDEData; export;
  134. var
  135.   ThisWindow: PDDEServerWindow;
  136.   ItemNum   : Integer;
  137. begin
  138.   CallbackProc := 0;    { See if proved otherwise }
  139.  
  140.   ThisWindow := PDDEServerWindow(App.MainWindow);
  141.  
  142.   case CallType of
  143.  
  144.     xtyp_WildConnect:
  145.       CallbackProc := ThisWindow^.WildConnect(HSz1, HSz2, Fmt);
  146.  
  147.     xtyp_Connect:
  148.       if Conv = 0 then
  149.       begin
  150.         if ThisWindow^.MatchTopicAndService(HSz1, HSz2) then
  151.           CallbackProc := 1;   { Connected! }
  152.       end;
  153. { When a connection is confirmed, record the conversation handle as the
  154.   window's own.
  155. }
  156.     xtyp_Connect_Confirm:
  157.       ThisWindow^.ConvHdl := Conv;
  158.  
  159. { The client has requested data, either as a direct request or
  160.   in response to an advisory.  Return the current state of the
  161.   data.
  162. }
  163.     xtyp_AdvReq, xtyp_Request:
  164.       begin
  165.         ItemNum := ThisWindow^.MatchTopicAndItem(HSz1, HSz2);
  166.         if ItemNum > 0 then
  167.           CallbackProc := ThisWindow^.DataRequested(CallType, ItemNum, Fmt);
  168.       end;
  169.  
  170. { Respond to Poke requests ... this demo only allows Pokes of DataItem1.
  171.   Return dde_FAck to acknowledge the receipt, 0 otherwise.
  172. }
  173.     xtyp_Poke:
  174.       begin
  175.         if ThisWindow^.AcceptPoke(HSz2, Fmt, Data) then
  176.           CallbackProc := dde_FAck;
  177.       end;
  178.  
  179. { The client has requested the start of an advisory loop.  Note
  180.   that we assume a "hot" loop.  Set the Advising flag to indicate
  181.   the open loop, which will be checked whenever the data is changed.
  182. }
  183.     xtyp_AdvStart:
  184.       begin
  185.         ItemNum := ThisWindow^.MatchTopicAndItem(HSz1, HSz2);
  186.         if ItemNum > 0 then
  187.         begin
  188.           if NumAdvLoops < MaxAdvisories then    { Arbitrary number }
  189.           begin
  190.             Inc(NumAdvLoops);
  191.             ThisWindow^.Advising[ItemNum] := True;
  192.             CallbackProc := 1;
  193.           end;
  194.         end;
  195.       end;
  196.  
  197. { The client has requested the advisory loop to terminate.
  198. }
  199.     xtyp_AdvStop:
  200.       begin
  201.         ItemNum := ThisWindow^.MatchTopicAndItem(HSz1, HSz2);
  202.         if ItemNum > 0 then
  203.         begin
  204.           if NumAdvLoops > 0 then
  205.           begin
  206.             Dec(NumAdvLoops);
  207.             if NumAdvLoops = 0 then
  208.               ThisWindow^.Advising[ItemNum] := False;
  209.             CallbackProc := 1;
  210.           end;
  211.         end;
  212.       end;
  213.   end;  { Case CallType }
  214.  
  215. end;
  216.  
  217.  
  218. { TDDEServerWindow Methods }
  219.  
  220. { Constructs an instance of the DDE Server Window.  Calls on the
  221.   inherited constructor, then sets up this objects own instandce
  222.   data.
  223. }
  224. constructor TDDEServerWindow.Init(AParent: PWindowsObject; ATitle: PChar);
  225. var
  226.   I : Integer;
  227. begin
  228.   TWindow.Init(AParent, ATitle);
  229.  
  230.   Inst      := 0;      { Must be zero for first call to DdeInitialize }
  231.   @CallBack := nil;    { MakeProcInstance is called in SetupWindow    }
  232.  
  233.   for I := 1 to NumValues do
  234.   begin
  235.     DataSample[I]:= 0;
  236.     Advising[I]  := False;
  237.   end;
  238. end;
  239.  
  240. { Destroys an instance of the DDE Server Window.  Checks to see if the
  241.   Callback Proc Instance had been created, and frees it if so.  Also 
  242.   calls DdeUninitialize to terminate the conversation.  Then just calls
  243.   on the ancestral destructor to finish.
  244. }
  245. destructor TDDEServerWindow.Done;
  246. var
  247.   I : Integer;
  248. begin
  249.   if ServiceHSz <> 0 then
  250.     DdeFreeStringHandle(Inst, ServiceHSz);
  251.   if TopicHSz <> 0 then
  252.     DdeFreeStringHandle(Inst, TopicHSz);
  253.   for I := 1 to NumValues do
  254.     if ItemHSz[I] <> 0 then
  255.       DdeFreeStringHandle(Inst, ItemHSz[I]);
  256.  
  257.   if Inst <> 0 then
  258.     DdeUninitialize(Inst);   { Ignore the return value }
  259.     
  260.   if @CallBack <> nil then
  261.     FreeProcInstance(@CallBack);
  262.  
  263.   TWindow.Done;
  264. end;
  265.  
  266. { Redefines GetWindowClass to give this application its own Icon and
  267.   default menu.
  268. }
  269. procedure TDDEServerWindow.GetWindowClass(var AWndClass: TWndClass);
  270. begin
  271.   TWindow.GetWindowClass(AWndClass);
  272.   AWndClass.hIcon := LoadIcon(AWndClass.hInstance, PChar(id_Icon));
  273.   AWndClass.lpszMenuName := PChar(id_Menu);
  274. end;
  275.  
  276. { Returns the class name of this window.  This is necessary since we
  277.   redefine the inherited GetWindowClass method, above.
  278. }
  279. function TDDEServerWindow.GetClassName: PChar;
  280. begin
  281.   GetClassName := 'TDDEServerWindow';
  282. end;
  283.  
  284. { Completes the initialization of the DDE Server Window.  Initializes
  285.   the use of the DDEML by registering the services provided by this
  286.   application.  Recall that the actual names used to register are
  287.   defined in a separate unit (DataEntry), so that they can be used
  288.   by the client as well.
  289. }
  290. procedure TDDEServerWindow.SetupWindow;
  291. var
  292.   I : Integer;
  293. begin
  294.   TWindow.SetupWindow;
  295.  
  296.   @CallBack:= MakeProcInstance(@CallBackProc, HInstance);
  297.  
  298.   if DdeInitialize(Inst, CallBack, 0, 0) = dmlErr_No_Error then
  299.   begin
  300.     ServiceHSz:= DdeCreateStringHandle(Inst, DataEntryName, cp_WinAnsi);
  301.     TopicHSz  := DdeCreateStringHandle(Inst, DataTopicName, cp_WinAnsi);
  302.     for I := 1 to NumValues do
  303.       ItemHSz[I] := DdeCreateStringHandle(Inst, DataItemNames[I],
  304.         cp_WinAnsi);
  305.  
  306.     if DdeNameService(Inst, ServiceHSz, 0, dns_Register) = 0 then
  307.     begin
  308.       MessageBox(HWindow, 'Registration failed.', Application^.Name,
  309.         mb_IconStop);
  310.       PostQuitMessage(0);
  311.     end;
  312.   end
  313.   else
  314.     PostQuitMessage(0);
  315. end;
  316.  
  317. procedure TDDEServerWindow.Paint(PaintDC: HDC; var PaintInfo: TPaintStruct);
  318. type
  319.   TDataItem = record
  320.     Name: Pointer;
  321.     Value: Integer;
  322.   end;
  323.   TData = array[1..NumValues] of TDataItem;
  324. var
  325.   R: TRect;
  326.   S: array[0..255] of Char;
  327.   S1: array[0..512] of Char;
  328.   Len, I: Integer;
  329.   Data: TData;
  330. begin
  331.   GetClientRect(HWindow, R);
  332.   InflateRect(R, -10, 0);
  333.   LoadString(hInstance, st_Message, S, SizeOf(S));
  334.   for I := 1 to NumValues do
  335.   begin
  336.     Data[I].Name := DataItemNames[I];
  337.     Data[I].Value := DataSample[I];
  338.   end;
  339.   Len := wvsPrintf(S1, S, Data);
  340.   DrawText(PaintDC, S1, Len, R, dt_WordBreak);
  341. end;
  342.  
  343. { Returns True if the given Topic and Service match those supported
  344.   by this application.  False otherwise.
  345. }
  346. function TDDEServerWindow.MatchTopicAndService(Topic, Service: HSz): Boolean;
  347. begin
  348.   MatchTopicAndService := False;
  349.   if DdeCmpStringHandles(TopicHSz, Topic) = 0 then
  350.     if DdeCmpStringHandles(ServiceHSz, Service) = 0 then
  351.       MatchTopicAndService := True;
  352. end;
  353.  
  354. { Determines if the given Topic and Item match one supported by this
  355.   application.  Returns the Item Number of the supported item (in the
  356.   range 1..NumValues) if one is found, and zero if no match.
  357. }
  358. function TDDEServerWindow.MatchTopicAndItem(Topic, Item: HSz): Integer;
  359. var
  360.   I : Integer;
  361. begin
  362.   MatchTopicAndItem := 0;
  363.   if DdeCmpStringHandles(TopicHSz, Topic) = 0 then
  364.     for I := 1 to NumValues do
  365.       if DdeCmpStringHandles(ItemHSz[I], Item) = 0 then
  366.         MatchTopicAndItem := I;
  367. end;
  368.  
  369. { Responds to wildcard connect requests.  These requests are generated
  370.   whenever a client tries to connect to a server with either service or topic
  371.   name set to 0.  If a server detects a wild card match, it returns a
  372.   handle to an array of THSZPair's containing the matching supported Service
  373.   and Topic.
  374. }
  375. function TDDEServerWindow.WildConnect(Topic, Service: HSz;
  376.   ClipFmt: Word): HDDEData;
  377. var
  378.   TempPairs: array [0..1] of THSZPair;
  379.   Matched  : Boolean;
  380. begin
  381.   TempPairs[0].hszSvc  := ServiceHSz;
  382.   TempPairs[0].hszTopic:= TopicHSz;
  383.   TempPairs[1].hszSvc  := 0;     { 0-terminate the list }
  384.   TempPairs[1].hszTopic:= 0;
  385.  
  386.   Matched := False;
  387.  
  388.   if (Topic= 0) and (Service = 0) then
  389.     Matched := True                    { Complete wildcard }
  390.   else
  391.     if (Topic = 0) and (DdeCmpStringHandles(Service, ServiceHSz) = 0) then
  392.       Matched := True
  393.     else
  394.       if (DdeCmpStringHandles(Topic, TopicHSz) = 0) and (Service = 0) then
  395.         Matched := True;
  396.  
  397.   if Matched then
  398.     WildConnect := DdeCreateDataHandle(Inst, @TempPairs, SizeOf(TempPairs),
  399.       0, 0, ClipFmt, 0)
  400.   else
  401.     WildConnect := 0;
  402. end;
  403.  
  404. { Accepts and acts upon Poke requests from the Client.  For this
  405.   demonstration, allows only the value of DataItem1 to be changed by a Poke.
  406. }
  407. function TDDEServerWindow.AcceptPoke(Item: HSz; ClipFmt: Word;
  408.   Data: HDDEData): Boolean;
  409. var
  410.   DataStr   : TDataString;
  411.   Err       : Integer;
  412.   TempSample: TDataSample;
  413. begin
  414.   if (DdeCmpStringHandles(Item, ItemHSz[1]) = 0) and (ClipFmt = cf_Text) then
  415.   begin
  416.     DdeGetData(Data, @DataStr, SizeOf(DataStr), 0);
  417.     Val(DataStr, TempSample[1], Err);
  418.  
  419.     if TempSample[1] <> DataSample[1] then
  420.     begin
  421.       DataSample[1] := TempSample[1];
  422.       if Advising[1] then
  423.         DdePostAdvise(Inst, TopicHSz, ItemHSz[1]);
  424.     end;
  425.     InvalidateRect(HWindow, nil, True);
  426.     AcceptPoke := True;
  427.   end
  428.   else
  429.     AcceptPoke := False;
  430. end;
  431.  
  432. { Returns the data requested by the given TransType and ClipFmt values.
  433.   This could happen either in response to either an xtyp_Request or an
  434.   xtyp_AdvReq.  The ItemNum parameter indicates which of the supported
  435.   items (in the range 1..NumValues) was requested (note that this method
  436.   assumes that the caller has already established validity and ID of the
  437.   requested item using MatchTopicAndItem).  The corresponding data from
  438.   the DataSample instance variable is converted to text and returned.
  439. }
  440. function TDDEServerWindow.DataRequested(TransType: Word; ItemNum: Integer;
  441.   ClipFmt: Word): HDDEData;
  442. var
  443.   ItemStr: TDataString;   { Defined in DataEntry.TPU }
  444. begin
  445.   if ClipFmt = cf_Text then
  446.   begin
  447.     Str(DataSample[ItemNum], ItemStr);
  448.     DataRequested := DdeCreateDataHandle(Inst, @ItemStr, StrLen(ItemStr) + 1,
  449.       0, ItemHSz[ItemNum], ClipFmt, 0);
  450.   end
  451.   else
  452.     DataRequested := 0;
  453. end;
  454.  
  455. { Activates the data-entry dialog, and updates the stored
  456.   data when complete.
  457. }
  458. procedure TDDEServerWindow.CMDataEnter(var Msg: TMessage);
  459. const
  460.   ValEditIds : array [1..NumValues] of Integer = (id_Value1,
  461.     id_Value2, id_Value3);
  462. var
  463.   DataEntry  : PDialog;
  464.   Err, I     : Integer;
  465.   TempSample : TDataSample;
  466.   Ed         : PEdit;
  467.   TransferRec: array [1..NumValues] of record
  468.                                          ValStr : array [0..19] of Char;
  469.                                        end;
  470. begin
  471.   DataEntry := New(PDialog, Init(@Self, 'DATAENTRY'));
  472.  
  473.   for I := 1 to NumValues do
  474.   begin
  475.     Str(DataSample[I], TransferRec[I].ValStr);
  476.     New(Ed, InitResource(DataEntry, ValEditIds[I],
  477.       SizeOf(TransferRec[I].ValStr)));
  478.   end;
  479.  
  480.   DataEntry^.TransferBuffer := @TransferRec;
  481.  
  482.   if Application^.ExecDialog(DataEntry) = IdOK then
  483.   begin
  484.     for I := 1 to NumValues do
  485.     begin
  486.       Val(TransferRec[I].ValStr, TempSample[I], Err);
  487.  
  488.       if TempSample[I] <> DataSample[I] then
  489.       begin
  490.         DataSample[I] := TempSample[I];
  491.         if Advising[I] then
  492.           DdePostAdvise(Inst, TopicHSz, ItemHSz[I]);
  493.       end;
  494.     end;
  495.     InvalidateRect(HWindow, nil, True);
  496.   end;
  497. end;
  498.  
  499. { Clears the current data.
  500. }
  501. procedure TDDEServerWindow.CMDataClear(var Msg: TMessage);
  502. var
  503.   I : Integer;
  504. begin
  505.   for I := 1 to NumValues do
  506.   begin
  507.     DataSample[I] := 0;
  508.     if Advising[I] then
  509.       DdePostAdvise(Inst, TopicHSz, ItemHSz[I]);
  510.   end;
  511.   InvalidateRect(HWindow, nil, True);
  512. end;
  513.  
  514. { Posts the about box dialog for the DDE Server.
  515. }
  516. procedure TDDEServerWindow.CMHelpAbout(var Msg: TMessage);
  517. begin
  518.   Application^.ExecDialog(New(PDialog, Init(@Self, PChar(id_About))));
  519. end;
  520.  
  521.  
  522. { TDDEServerApp Methods }
  523.  
  524. procedure TDDEServerApp.InitMainWindow;
  525. begin
  526.   MainWindow := New(PDDEServerWindow, Init(nil, Application^.Name));
  527. end;
  528.  
  529.  
  530. { Main program }
  531.  
  532. begin
  533.   App.Init(DemoTitle);
  534.   App.Run;
  535.   App.Done;
  536. end.
  537.  
  538.